package util

import (
	"fmt"
	"gateway/src/constant"
	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
	"log"
	"os"
	"os/exec"
	"runtime"
)

var ProxyConfigs = make(map[string]string)
var LoadBalanceList = make(map[string]*LoadBalance)

type EnvConfig *os.File

// 引入包时会自动引入init函数
func init() {
	// 延迟处理的函数
	defer func() {
		// 发生宕机时，获取panic传递的上下文并打印
		err := recover()
		switch err.(type) {
		case runtime.Error: // 运行时错误
			log.Panicln("runtime error:", err)
		case exec.Error:
			log.Fatal("error:", err) // 非运行时错误

		}
	}()
	viper.SetConfigName("proxy")         //把json文件换成yaml文件，只需要配置文件名 (不带后缀)即可
	viper.AddConfigPath("src/configure") //添加配置文件所在的路径
	viper.SetConfigType("yaml")          //设置配置文件类型
	err := viper.ReadInConfig()
	if err != nil {
		fmt.Printf("config file error: %s\n", err)
		os.Exit(1)
	}
	viper.WatchConfig() //监听配置变化
	viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("配置发生变更：", e.Name) })
	/**

	如果要读取cache1下的max-items，只需要执行viper.Get(“app.cache1.max-items”)就可以了。


	Unmarshal(rawVal interface{}) : error UnmarshalKey(key string, rawVal interface{}) : error var config Config var mysql MySQL err := Unmarshal(&config) // 将配置解析到 config 变量 if err != nil { t.Fatalf("unable to decode into struct, %v", err) } err := UnmarshalKey("mysql", &mysql) // 将配置解析到 mysql 变量 if err != nil { t.Fatalf("unable to decode into struct, %v", err) }

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
	  // 配置文件发生变更之后会调用的回调函数
		fmt.Println("Config file changed:", e.Name)
	})
	*/
	resultMap := viper.GetStringMap("proxy")
	ProxyConfigs["hasRegister"] = viper.GetString("gateway.hasRegister")
	ProxyConfigs["port"] = viper.GetString("server.port")
	if ProxyConfigs["hasRegister"] == "" {
		log.Fatal("hasRegister could not be null")
		return
	}
	if ProxyConfigs["port"] == "" {
		log.Fatal("server port could not be null")

	}
	if len(resultMap) != 0 {
		//fmt.Println("test:",resultMap[0])
		//循环proxy里面的配置信息
		log.Println("开始读取配置信息，创建负载均衡器")
		for k, _ := range resultMap {
			fmt.Println(k)
			//提取里面的每一个负载均衡配置，并构造负载均衡器
			serverMap := viper.GetStringMap("proxy." + k)
			//先提取构建负载均衡所需参数
			successCount := 0
			failMaxNum := 0
			checkTime := 0
			loadBalanceWay := ""
			failFactor := 0.0
			recoverFactor := 0.0
			head := "" //用于访问该组路由内的服务时使用（作为标识），使用该头部访问的将路由到该负载均衡器内，并根据负载均衡器配置选择合适的服务
			//根据配置信息创建负载均衡负责的服务

			var serverSlice = make(HttpServerSlice, 0)
			//遍历负载均衡配置里的服务信息和负载均衡信息
			//原来interface{}转换类型时会自动读取的，不需要我自行再次判断
			for k1, v1 := range serverMap {
				//delete(serverMap, k1)
				fmt.Println(k1, v1)
				//先提取构建负载均衡所需参数 viper这个包好像是自己转成小写，因此这里也写小写，暂时没有封装
				if k1 == "successcount" {
					successCount = v1.(int) //interface{}转成其他类型均类似操作
				} else if k1 == "recoverfactor" {
					recoverFactor = v1.(float64) //interface{}转成其他类型均类似操作
				} else if k1 == "checktime" {
					checkTime = v1.(int) //interface{}转成其他类型均类似操作
				} else if k1 == "loadbalanceway" {
					loadBalanceWay = v1.(string) //interface{}转成其他类型均类似操作
				} else if k1 == "failmaxnum" {
					failMaxNum = v1.(int) //interface{}转成其他类型均类似操作
				} else if k1 == "failfactor" {
					failFactor = v1.(float64) //interface{}转成其他类型均类似操作
				} else if k1 == "head" {
					head = v1.(string) //interface{}转成其他类型均类似操作
				} else {
					serverMsg := v1.(map[string]interface{})
					serverName := serverMsg["servername"].(string)
					if serverName == "" {
						log.Fatal("serverName could not be null")
						return
					}
					//权重只有在负载均衡方式为平滑加权轮询（非默认值时 sw-polling）才为必填项
					var weight = 0
					weight = serverMsg["weight"].(int)
					if loadBalanceWay == "sw-polling" && weight == 0 {
						log.Fatal("weight could not be null")
						return
					}
					path := serverMsg["path"].(string) //用于想访问指定服务器时使用，直接路由到该服务，不再进行负载均衡。
					pass := serverMsg["pass"].(string)
					//当不使用注册中心时该项为必填
					if ProxyConfigs["hasRegister"] == "false" && pass == "" {
						log.Fatal("pass could not be null")
						return
					}
					//遍历创建服务信息放入切片中
					serverSlice = append(serverSlice, NewHttpServer(serverName, pass, weight, path))
				}
			}
			//开始创建负载均衡器
			//先判断必要参数是否合理
			if loadBalanceWay == "" {
				//使用默认值
				loadBalanceWay = constant.LoadBalanceDefault["loadBalanceWay"].(string)
			}
			//当未使用注册中心时需检测以下部分 如未填写则均使用默认值 //这些部分以后可以优化，go责任链？
			if ProxyConfigs["hasRegister"] == "false" {
				//当不连接注册中心时，检测检查时间是否补充，如未填写则使用默认值
				if checkTime == 0 {
					checkTime = constant.LoadBalanceDefault["checkTime"].(int)
				}
				//当使用普通轮询时需要检测以下部分
				if loadBalanceWay == "polling" {
					if failMaxNum == 0 {
						failMaxNum = constant.LoadBalanceDefault["failMaxNum"].(int)
					}
					if successCount == 0 {
						successCount = constant.LoadBalanceDefault["successCount"].(int)
					}

				} else if loadBalanceWay == "sw-polling" { //平滑加权轮询
					if failFactor == 0.0 {
						failFactor = constant.LoadBalanceDefault["failFactor"].(float64)
					}
					if recoverFactor == 0.0 {
						recoverFactor = constant.LoadBalanceDefault["recoverFactor"].(float64)
					}
				}
				//后续补充在这里添加
				//检测完毕后开始创建负载均衡器，并启动健康检查
				log.Println("通过所有校检，开始创建负载均衡器，并启动健康检查")
				lb := NewLoadBalance(serverSlice, loadBalanceWay)
				lb.StartCheck(failMaxNum, successCount, failFactor, recoverFactor, checkTime)
				LoadBalanceList[head] = lb
			}
			//使用注册中心的版本后续补充，该部分代码可优化空间很大。
		} //读取配置信息，创建负载均衡器结束
		log.Println(LoadBalanceList)
		log.Println("创建负载均衡器结束")

	}
}
